core: parseArea helper + flow area through BacklogItem and BuilderOverview (PIR #819)#876
Merged
Conversation
…e about label names)
…@cluesmith/codev-core
# Conflicts: # packages/codev/src/agent-farm/servers/overview.ts # packages/types/src/api.ts
…rea import example)
amrmelsayed
added a commit
that referenced
this pull request
May 27, 2026
Closed
9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PIR Review:
parseAreahelper +areafield onBacklogItemandBuilderOverviewFixes #819
Summary
Adds the
parseAreahelper (inpackages/codev/src/lib/github.ts) that extracts a singlearea/*value from an issue's labels, and threads that value as a requiredarea: stringfield throughBacklogItem/BuilderOverview(server-internal) andOverviewBacklogItem/OverviewBuilder(wire contracts). This is pure scaffolding for two follow-up consumers — #811 (backlog grouping by area) and #818 (builders-tree grouping by area). No user-visible behavior change; the new field appears on every/api/overviewpayload but no UI surface reads it yet.Files Changed
Computed via
git diff --stat origin/main...HEAD(branch-only — excludes commits picked up from main during a mid-PIR merge):codev/plans/819-core-parsearealabels-helper-fl.md(+279 / -0)codev/projects/819-core-parsearealabels-helper-fl/status.yaml(+27 / -0) — porch-managed, not hand-editedcodev/reviews/819-core-parsearealabels-helper-fl.md(+90 / -0) — this filecodev/state/pir-819_thread.md(+79 / -0)packages/codev/src/__tests__/github.test.ts(+66 / -0)packages/codev/src/agent-farm/servers/overview.ts(+38 / -5)packages/codev/src/lib/github.ts(+29 / -0)packages/core/src/constants.ts(+9 / -0)packages/types/src/api.ts(+15 / -0)packages/vscode/src/test/builders.test.ts(+1 / -0)Total: 10 files, +628 / -5.
Commits
Implementation:
da040105[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Add parseAreaLabels helper + unit testsfc8b3001[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Add resolvePrimaryArea helper + tests763d8170[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Wire areas[] through BacklogItem and BuilderOverview6e90f5c6[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Add areas[] to OverviewBuilder and OverviewBacklogItem wire types65739680[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Thread: log implement-phase progressDesign revisions at dev-approval (two rounds — see Things to Look At):
114aee99[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Revise: parseArea returns single string (drop array shape)df442ca8[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Revise: drop resolvePrimaryArea (parser projects to single area now)5c8800f8[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Revise: BacklogItem.area + BuilderOverview.area (single string)7cf2d8cb[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Revise: OverviewBuilder.area + OverviewBacklogItem.area wire fields2aa42101[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Document design revision in plan + threadf638e84c[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Revise: drop cross-cutting privilege (parser is policy-free about label names)62df91f7[PIR core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819] Extract 'Uncategorized' to UNCATEGORIZED_AREA constant in @cluesmith/codev-coreTest Results
pnpm -w build: ✓ pass (full workspace incl. types, core, codev, dashboard)pnpm --filter @cluesmith/codev test src/__tests__/github.test.ts: ✓ pass (66 tests, 10 new forparseArea)pnpm --filter @cluesmith/codev test(full suite): ✓ pass (3149 tests, 13 pre-existing skips, no regressions introduced)pnpm --filter codev-vscode run check-types: ✓ pass (verifies the wire-type addition propagates through vscode without compile errors)dev-approvalgate): the human inspected the running implementation and approved.Architecture Updates
No changes to
codev/resources/arch.md. The PR adds a parser + a required field on existing wire-contract shapes; it doesn't introduce new module boundaries, new caching layers, new endpoints, or new architectural patterns. The cache discipline question raised in the issue body (defensive??= []at serve-out) was investigated and found to be structurally satisfied —OverviewCachealready holds only raw forge responses, never derivedBacklogItem/BuilderOverviewshapes, so the "stale cache entry missingarea" failure mode isn't reachable. This is a finding about existing architecture, not a change to it, so it lives in this review and the implement-phase thread rather than inarch.md.Lessons Learned Updates
No additions to
codev/resources/lessons-learned.md, but two durable principles emerged that landed in the project's memory system instead (which is the appropriate home for AI-collaboration-shape lessons):Framework code must be policy-free about specific label values. The initial parser implementation privileged
area/cross-cutting(returning it preferentially when present). The user pushed back — Codev framework code shouldn't bake in semantic conventions about specific label names; teams using Codev decide their own labeling conventions. Stripped the privilege; added a no-privilege regression-guard test. Captured asfeedback_framework_neutral_on_label_semantics.md.Wire-shape "permissiveness then projection" is a smell. The initial design returned
string[]fromparseAreaLabelsand then collapsed it to a single bucket via a separateresolvePrimaryAreahelper at the UI boundary — two operations cancelling each other out. The cleaner shape is for the parser to do the projection once at the boundary and returnstringdirectly, symmetric withparseLabelDefaults's single-stringtype/priorityreturns. This isn't a generalizable arch principle (it's specific to this case), so it's documented in the plan revision notes and this review rather thanlessons-learned.md.codev/resources/arch.mdandcodev/resources/lessons-learned.mdwould be updated as a follow-up by the MAINTAIN protocol's quarterly sweep if either principle generalizes further; nothing about this PR warrants forcing them into the docs now.Things to Look At During PR Review
Two rounds of design revision during the implement phase — visible in the commit history. The original implementation followed the issue body verbatim (
areas: string[]+resolvePrimaryAreahelper, withcross-cuttingprivilege). The human at dev-approval flagged two issues and the design collapsed to single-string at the parser with no special-cased label names. The final shape is meaningfully smaller and cleaner than what the issue body proposed — see the plan file's revision note for the full reasoning.The
discoverBuildersdefaulting pattern. Threebuilders.push({...})sites indiscoverBuilderseach setarea: UNCATEGORIZED_AREA. ThegetOverviewenrichment loop then overrides this withparseArea(issue.labels)for builders whose issue is in the cached issue list. Builders withissueId: null(soft-mode / task-mode) keep the default. Worth a glance to confirm the defaulting + enrichment flow is what you'd expect; one alternative would be to make the fieldarea: string | nulland let the UI render its own fallback, but'Uncategorized'as a server-side default keeps consumers free of null-handling.OverviewCachedoes not cache derived shapes — verified atoverview.ts:763-769. Only rawForgePR[]/ForgeIssueListItem[]are cached.BacklogItemandBuilderOvervieware rebuilt fresh on everygetOverviewcall, soparseArearuns against current labels every time. The "stale cache entry missingarea" concern from the issue body §B isn't reachable in the current architecture. If a future change ever adds a derived-shape cache, that discipline would need to be re-applied at that point.Two follow-up issues filed during this PIR:
type:/priority:/area/is a real engineering concern worth resolving globally; this PIR ships the slash convention as-spec'd; Label namespace separator: resolve mixed colon-vs-slash convention #869 lays out options A/B/C for the wider question).Overview*/*Overviewtypes" (five paired interfaces acrosspackages/codev/src/agent-farm/servers/overview.tsandpackages/types/src/api.tsare structurally identical; this PIR'sareaaddition had to land in both halves of the pair, demonstrating the drift cost; proposed fix is to make@cluesmith/codev-typesthe single source of truth).parseAreaprojection rule: first-alphabetical wins, no label name is privileged,'Uncategorized'fallback. The no-privilege test explicitly usesarea/cross-cuttingas fixture data to prove the parser doesn't treat it specially — that's intentional and is the regression guard against re-introducing the privilege.3-Way Consultation Dispositions
Single advisory pass (PIR's
max_iterations: 1). Verdicts:import { parseArea } from '@cluesmith/codev'butparseAreaisn't exported from the package root. Both addressed in this same review file revision — see the corrected Files Changed list and the updated "How to Test Locally" section.consultexited code 1 with opaque[object Object]error on three attempts; no output file written). Surfaced to the architect; no model verdict obtained for this PR.No
REQUEST_CHANGESfindings. Codex's COMMENT findings were minor accuracy issues on the review file (not on the code) and were corrected in place.How to Test Locally
For reviewers pulling the branch:
pir-819→ Review Diff (auto-detects the repo's default branch). Orgit diff main...HEAD.afx dev pir-819from a shell.pnpm -w buildis green.pnpm --filter @cluesmith/codev test src/__tests__/github.test.ts— 66 tests pass, including the newparseAreablock./api/overviewon the running dev server:curl http://localhost:<port>/api/overview | jq '.backlog[0] | {id, area}'. Every backlog entry should have a populatedareastring. This issue (core: parseAreaLabels helper + flow areas[] through BacklogItem and BuilderOverview #819) is labeledarea/core, so its entry should show"area": "core".curl http://localhost:<port>/api/overview | jq '.builders[] | {id, issueId, area}'. Builders with anissueIdmatching a labeled issue inherit that issue's area; builders without an issue (soft-mode / task-mode) show"area": "Uncategorized".parseAreais currently internal to the codev server (no public re-export from the package root). To exercise edge cases directly, the cleanest path is the unit test file atpackages/codev/src/__tests__/github.test.ts— runpnpm --filter @cluesmith/codev test src/__tests__/github.test.ts, or add a temporary case there. The 10 included cases already covernull,'',[{name: 'area/auth'}, {name: 'area/cross-cutting'}](returns'auth'— first alphabetical, cross-cutting not privileged), and the other defensive paths.